// 专门负责解析模板内容
class Compile {
    // 参数1: 模板
    // 参数2: vue实例
    constructor(el,vm){
        //el 是 new Vue 传递的选择器
        this.el = typeof el == 'string'? document.querySelector(el):el
        //vm: new 的Vue实例
        this.vm = vm;

        // 编译模板
        if(this.el) {
            // 把el中所有子节点都放入到内存中 fragment
           let fragment =  this.nodeToFragment(this.el)
            
            // 在内存中编译fragment 
            this.compile(fragment)
            // 把fragement 一次性的添加到页面
            this.el.appendChild(fragment)
        }
    }
    nodeToFragment(node) {
        let fragment = document.createDocumentFragment();
        //把el中所有的子节点挨个添加到文档碎片中
        let childnodes = node.childNodes;
        this.toArrary(childnodes).forEach(element => {
            //把所有的节点都添加到fragment中
            fragment.appendChild(element)
            
            
            
        });
       
        return fragment
    }
    // 工具方法
    toArrary(likeArrary){
       return [].slice.call(likeArrary)
    }
    isElementNode(node){
        // nodeType: 节点的类型  1.元素节点  3.文本节点
        return node.nodeType === 1
    }
    isTextNode(node){
        return node.nodeType === 3
    }
    // 解析标签
    compileElement(node){
        //1.获取到当前节点下的所有属性
        let attributes = node.attributes;
        // console.log(attributes)
        
        //2.解析vue的指令(以v-开头的属性)
        this.toArrary(attributes).forEach(attr=>{
            // console.dir(attr)
            let attrName = attr.nodeName;
           
            if(this.isDirective(attrName)){
                
                //拿到指令的类型
                let type = attrName.slice(2);
                let expr = attr.nodeValue; 
                if(this.isEventDirective(type)){
                   // 给当前元素注册事件即可
                   // 需要拿到事件类型
                //    let eventType = type.split(':')[1];
                //    console.log(eventType)
                //    node.addEventListener(eventType,this.vm.$methods[expr].bind(this.vm));  
                   CompileUtil['eventHandler'](node,this.vm,type,expr);
                }else{
                    CompileUtil[type] && CompileUtil[type](node,this.vm,expr);
                }
                
            }
        })
    }
    // 解析文本
    compileText(node){
       CompileUtil.mustache(node,this.vm);
    }
    // 编译文档碎片
    compile(fragment){
        let childnodes = fragment.childNodes;
        this.toArrary(childnodes).forEach(node=>{
            // 如果是元素需要解析指令，
            if(this.isElementNode(node)){
                this.compileElement(node)
            }
            // 如果是文本节点需要解析指令
            if(this.isTextNode(node)){
                this.compileText(node)
            }
            //如果当前节点还有子节点，需要递归的解析
             if(node.childNodes && node.childNodes.length > 0){
                 this.compile(node)
             }

        })
    }
    //判断是否是指令
    isDirective(attrName){
        return attrName.startsWith('v-')
    }
    isEventDirective(attrName){
        return attrName.split(':')[0] === 'on'
    }
}

let CompileUtil = {
    mustache(node,vm){
        let txt = node.textContent;
        // ()一个分组
        let reg = /\{\{(.+)\}\}/;
        console.log(txt)
        if(reg.test(txt)){
            // $1 代表第一个分组的内容
           let expr =  RegExp.$1;
         
           node.textContent = txt.replace(reg,this.getVMValue(vm,expr));
        }  
    },
    // 处理v-text指令  data['msg']
    text(node,vm,expr){
        node.textContent = this.getVMValue(vm,expr);
        
    },
    html(node,vm,expr){
        node.innerHTML = this.getVMValue(vm,expr);
    },
    model(node,vm,expr){
        node.value = this.getVMValue(vm,expr);
    },
    eventHandler(node,vm,type,expr){
        //给当前元素注册事件
        let eventType = type.split(':')[1];
        let fn = vm.$methods && vm.$methods[expr]; 
        if(eventType && fn){
          node.addEventListener(eventType,vm.$methods[expr].bind(vm));
        }
    },
    //这个方法用于获取VM中的数据
    getVMValue(vm,expr){
     
        // 获取到vm中的所有数据
        let data = vm.$data;
        
        expr.split('.').forEach(key=>{
            data = data[key]
        })
        return data 
    }
}